﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using MobileHealthService.Model.Responses;
using SouthApps.MobileHealth.SDK.Common;
using SouthApps.MobileHealth.SDK.Services;
using SouthApps.MobileHealth.SDK.DAL;
using SouthApps.MobileHealth.MobileHealthService.Common;
using SouthApps.MobileHealth.SDK.Common.Exceptions;
using SouthApps.MobileHealth.SDK.DAL.Model;

using MobileHealthServiceResources = Resources.Resources_es_ES;
using SouthApps.MobileHealth.SDK.Cache;
using SouthApps.Framework.LoggerService;
using System.Configuration;

namespace SouthApps.MobileHealth.MobileHealthService
{
    /// <summary>
    /// Summary description for MobileHealthService
    /// </summary>
    [WebService(Namespace = "http://southapps-ar/MobileHealthService/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    // [System.Web.Script.Services.ScriptService]
    public class MobileHealthService : System.Web.Services.WebService
    {
        private SecurityService securityService = new SecurityService();
        private FinanciadoresService financiadoresService = new FinanciadoresService();
        private AdministrationService administrationService = new AdministrationService();
        private ConveniosService conveniosService = new ConveniosService();
        private ZonasService zonasService = new ZonasService();
        static bool Initialized = false;

        [WebMethod]
        public LogonResponse Login(string username, string password, int financiadorId)
        {
            LoggerService.Log(LogLevel.Verbose, "[Login] method called.");

            LogonResponse response = new LogonResponse();

            try
            {
                User user = securityService.Login(username, password, financiadorId);

                if (user == null)
                {
                    response.ResponseCode = (byte)ResponseCode.WrongCredentials;
                    response.Message = MobileHealthServiceResources.InvalidUserNamePassword;

                    return response;
                }

                List<Permission> userPermissions = this.securityService.GetUserPermissions(user.userId);

                response.PermissionList = EntityTranslator.GetPermissionDtoArray(userPermissions);
                response.UserId = user.userId;
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [Login] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public AdministrationResponse GetAdministrationInfo(string platformName, int financiadorId)
        {
            LoggerService.Log(LogLevel.Verbose, "[GetAdministrationInfo] method called.");

            AdministrationResponse response = new AdministrationResponse();

            try
            {
                bool isLicensed = false;
                Platform platform = this.administrationService.GetPlatform(platformName);

                if (platform != null)
                {
                    isLicensed = this.administrationService.IsPlatformLicensed(financiadorId, platform.platformId);
                    response.Platform = EntityTranslator.GetPlatformDto(platform);
                }

                List<Server> servers = this.administrationService.GetServers();

                response.IsLicensed = isLicensed;
                response.ServerList = EntityTranslator.GetServerDtoArray(servers);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetAdministrationInfo] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public FinanciadorResponse GetFinanciador(int financiadorId)
        {
            FinanciadorResponse response = new FinanciadorResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetFinanciador] method called.");

            try
            {
                Financiador financiador = this.financiadoresService.GetFinanciador(financiadorId);
                response.Financiador = EntityTranslator.GetFinanciadorDto(financiador);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetSucursalesPrestadorCercanas] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public SucursalesPrestadorResponse GetSucursalesPrestadorCercanas(int financiadorId, int especialidadId, int planId, double latitud, double longitud, int pageNumber, int pageSize)
        {
            SucursalesPrestadorResponse response = new SucursalesPrestadorResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetSucursalesPrestadorCercanas] method called.");

            try
            {
                Location referenceLocation = new Location(latitud, longitud);
                int totalCount;
                List<ExtendedSucursalPrestador> sucursalesCercanas = this.financiadoresService.GetSucursalesPrestadorPorCercania(financiadorId, especialidadId, planId, referenceLocation, pageNumber, pageSize, out totalCount);

                response.TotalRecordCount = totalCount;
                response.SucursalPrestadorList = EntityTranslator.GetExtendedSucursalPrestadorDtoArray(sucursalesCercanas);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetSucursalesPrestadorCercanas] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public SucursalesPrestadorResponse GetSucursalesPrestadorPorZona(int financiadorId, int especialidadId, int planId, int departamentoId)
        {
            SucursalesPrestadorResponse response = new SucursalesPrestadorResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetSucursalesPrestadorPorZona] method called.");

            try
            {
                List<SucursalPrestador> sucursalesPorZona = this.financiadoresService.GetSucursalesPrestadorPorZona(financiadorId, especialidadId, planId, departamentoId);
                response.SucursalPrestadorList = EntityTranslator.GetSucursalPrestadorDtoArray(sucursalesPorZona);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetSucursalesPrestadorPorZona] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public GuardiasSucursalesPrestadorResponse GetGuardiasSucursalesPrestadorCercanas(int financiadorId, int? servicioGuardiaId, double latitud, double longitud, int pageNumber, int pageSize)
        {
            GuardiasSucursalesPrestadorResponse response = new GuardiasSucursalesPrestadorResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetGuardiasSucursalesPrestadorCercanas] method called.");

            try
            {
                Location referenceLocation = new Location(latitud, longitud);

                int totalCount;
                List<ExtendedGuardiaSucursalPrestador> guardiasSucursalesPrestadorCercanas = this.financiadoresService.GetGuardiasSucursalesPrestadorPorCercania(financiadorId, servicioGuardiaId, referenceLocation, pageNumber, pageSize, out totalCount);

                response.TotalRecordCount = totalCount;
                response.GuardiaSucursalPrestadorList = EntityTranslator.GetExtendedGuardiaSucursalPrestadorDtoArray(guardiasSucursalesPrestadorCercanas);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetGuardiasSucursalesPrestadorCercanas] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public GuardiasSucursalesPrestadorResponse GetGuardiasSucursalesPrestadorPorZona(int financiadorId, int? servicioGuardiaId, int departamentoId)
        {
            GuardiasSucursalesPrestadorResponse response = new GuardiasSucursalesPrestadorResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetGuardiasSucursalesPrestadorPorZona] method called.");

            try
            {
                List<GuardiaSucursalPrestador> guardiasSucursalesPorZona = this.financiadoresService.GetGuardiasSucursalesPrestadorPorZona(financiadorId, servicioGuardiaId, departamentoId);
                response.GuardiaSucursalPrestadorList = EntityTranslator.GetGuardiaSucursalPrestadorDtoArray(guardiasSucursalesPorZona);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetGuardiasSucursalesPrestadorPorZona] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public LocacionesServicioEspecialResponse GetLocacionesServicioEspecialCercanas(int financiadorId, int servicioEspecialId, double latitud, double longitud, int pageNumber, int pageSize)
        {
            LocacionesServicioEspecialResponse response = new LocacionesServicioEspecialResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetLocacionesServicioEspecialCercanas] method called.");

            try
            {
                int totalCount;
                
                List<LocacionServicioEspecial> locacionesServicioEspecial = this.financiadoresService.GetLocacionesServicioEspecialPorCercania(financiadorId, servicioEspecialId, new Location(latitud, longitud), pageNumber, pageSize, out totalCount);
                response.TotalRecordCount = totalCount;
                response.LocacionServicioEspecialList = EntityTranslator.GetLocacionServicioEspecialDtoArray(locacionesServicioEspecial);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetLocacionesServicioEspecialCercanas] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public LocacionesServicioEspecialResponse GetLocacionesServicioEspecialPorZona(int financiadorId, int departamentoId, int servicioEspecialId)
        {
            LocacionesServicioEspecialResponse response = new LocacionesServicioEspecialResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetLocacionesServicioEspecialPorZona] method called.");

            try
            {
                List<LocacionServicioEspecial> locacionesServicioEspecial = this.financiadoresService.GetLocacionesServicioEspecialPorZona(financiadorId, servicioEspecialId, departamentoId);
                response.LocacionServicioEspecialList = EntityTranslator.GetLocacionServicioEspecialDtoArray(locacionesServicioEspecial);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetLocacionesServicioEspecialPorZona] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public CentrosAtencionResponse GetCentrosAtencionCercanos(int financiadorId, double latitud, double longitud, int pageNumber, int pageSize)
        {
            CentrosAtencionResponse response = new CentrosAtencionResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetCentrosAtencionCercanos] method called.");

            try
            {
                int totalCount;
                List<ExtendedCentroAtencion> centrosAtencion = this.financiadoresService.GetCentrosAtencionPorCercania(financiadorId, new Location(latitud, longitud), pageNumber, pageSize, out totalCount);

                response.TotalRecordCount = totalCount;
                response.CentroAtencionList = EntityTranslator.GetExtendedCentroAtencionDtoArray(centrosAtencion);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetCentrosAtencionCercanos] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public CentrosAtencionResponse GetCentrosAtencionPorZona(int financiadorId, int departamentoId)
        {
            CentrosAtencionResponse response = new CentrosAtencionResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetCentrosAtencionCercanos] method called.");

            try
            {
                List<CentroAtencion> centrosAtencion = this.financiadoresService.GetCentrosAtencionPorZona(financiadorId, departamentoId);
                response.CentroAtencionList = EntityTranslator.GetCentroAtencionDtoArray(centrosAtencion);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetCentrosAtencionCercanos] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public EspecialidadesResponse GetEspecialidadesPlanFinanciador(int financiadorId, int planId)
        {
            EspecialidadesResponse response = new EspecialidadesResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetEspecialidadesPlanFinanciador] method called.");

            try
            {
                List<Especialidad> especialidadesFinanciador = this.conveniosService.GetEspecialidadesPlanFinanciador(financiadorId, planId);
                response.EspecialidadList = EntityTranslator.GetEspecialidadDtoArray(especialidadesFinanciador);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetEspecialidadesPlanFinanciador] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public ProvinciasResponse GetProvinciasConServicioEspecial(int financiadorId, int paisId, int servicioEspecialId)
        {
            ProvinciasResponse response = new ProvinciasResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetProvinciasConServicioEspecial] method called.");

            try
            {
                List<Provincia> provinciasServiciosEspeciales = this.zonasService.GetProvinciasConServicioEspecial(financiadorId, paisId, servicioEspecialId);
                response.ProvinciaList = EntityTranslator.GetProvinciaDtoArray(provinciasServiciosEspeciales);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetProvinciasConServicioEspecial] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public ProvinciasResponse GetProvinciasConGuardiasSucursalesPrestador(int financiadorId, int paisId, int? servicioGuardiaId)
        {
            ProvinciasResponse response = new ProvinciasResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetProvinciasConGuardiasSucursalesPrestador] method called.");

            try
            {
                List<Provincia> provinciasGuardias = this.zonasService.GetProvinciasConGuardiasSucursalesPrestador(financiadorId, paisId, servicioGuardiaId);
                response.ProvinciaList = EntityTranslator.GetProvinciaDtoArray(provinciasGuardias);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetProvinciasConGuardiasSucursalesPrestador] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public ProvinciasResponse GetProvinciasConEspecialidadSucursalesPrestador(int financiadorId, int paisId, int especialidadId)
        {
            ProvinciasResponse response = new ProvinciasResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetProvinciasConEspecialidadSucursalesPrestador] method called.");

            try
            {
                List<Provincia> provinciasSucursalesPrestador = this.zonasService.GetProvinciasConEspecialidadSucursalesPrestador(financiadorId, paisId, especialidadId);
                response.ProvinciaList = EntityTranslator.GetProvinciaDtoArray(provinciasSucursalesPrestador);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetProvinciasConEspecialidadSucursalesPrestador] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public ProvinciasResponse GetProvinciasConCentrosAtencion(int financiadorId, int paisId)
        {
            ProvinciasResponse response = new ProvinciasResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetProvinciasConCentrosAtencion] method called.");

            try
            {
                List<Provincia> provinciasCentrosAtencion = this.zonasService.GetProvinciasConCentrosAtencion(financiadorId, paisId);
                response.ProvinciaList = EntityTranslator.GetProvinciaDtoArray(provinciasCentrosAtencion);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetProvinciasConCentrosAtencion] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public DepartamentosResponse GetDepartamentosConEspecialidadSucursalesPrestador(int financiadorId, int provinciaId, int especialidadId)
        {
            DepartamentosResponse response = new DepartamentosResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetDepartamentosConEspecialidadSucursalesPrestador] method called.");

            try
            {
                List<Departamento> departamentosSucursalesPrestador = this.zonasService.GetDepartamentosConEspecialidadSucursalesPrestador(financiadorId, provinciaId, especialidadId);
                response.DepartamentoList = EntityTranslator.GetDepartamentoDtoArray(departamentosSucursalesPrestador);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetDepartamentosConEspecialidadSucursalesPrestador] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public DepartamentosResponse GetDepartamentosConGuardiasSucursalesPrestador(int financiadorId, int provinciaId, int? servicioGuardiaId)
        {
            DepartamentosResponse response = new DepartamentosResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetDepartamentosConGuardiasSucursalesPrestador] method called.");

            try
            {
                List<Departamento> departamentosGuardias = this.zonasService.GetDepartamentosConGuardiasSucursalesPrestador(financiadorId, provinciaId, servicioGuardiaId);
                response.DepartamentoList = EntityTranslator.GetDepartamentoDtoArray(departamentosGuardias);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetDepartamentosConGuardiasSucursalesPrestador] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public DepartamentosResponse GetDepartamentosConServicioEspecial(int financiadorId, int provinciaId, int servicioEspecialId)
        {
            DepartamentosResponse response = new DepartamentosResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetDepartamentosConServicioEspecial] method called.");

            try
            {
                List<Departamento> departamentosServiciosEspeciales = this.zonasService.GetDepartamentosConServicioEspecial(financiadorId, provinciaId, servicioEspecialId);
                response.DepartamentoList = EntityTranslator.GetDepartamentoDtoArray(departamentosServiciosEspeciales);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetDepartamentosConServicioEspecial] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public DepartamentosResponse GetDepartamentosConCentrosAtencion(int financiadorId, int provinciaId)
        {
            DepartamentosResponse response = new DepartamentosResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetDepartamentosConCentrosAtencion] method called.");

            try
            {
                List<Departamento> departamentosCentrosAtencion = this.zonasService.GetDepartamentosConCentrosAtencion(financiadorId, provinciaId);
                response.DepartamentoList = EntityTranslator.GetDepartamentoDtoArray(departamentosCentrosAtencion);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetDepartamentosConCentrosAtencion] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public PlanesResponse GetPlanesFinanciador(int financiadorId)
        {
            PlanesResponse response = new PlanesResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetPlanesFinanciador] method called.");

            try
            {
                List<Plan> planesFinanciador = this.conveniosService.GetPlanesFinanciador(financiadorId);
                response.PlanList = EntityTranslator.GetPlanDtoArray(planesFinanciador);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetPlanesFinanciador] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public ServiciosGuardiaResponse GetServiciosGuardiaFinanciador(int financiadorId)
        {
            ServiciosGuardiaResponse response = new ServiciosGuardiaResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetServiciosGuardiaFinanciador] method called.");

            try
            {
                List<ServicioGuardia> serviciosGuardiaFinanciador = this.financiadoresService.GetServiciosGuardiaFinanciador(financiadorId);
                response.ServicioGuardiaList = EntityTranslator.GetServicioGuardiaDtoArray(serviciosGuardiaFinanciador);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetServiciosGuardiaFinanciador] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public ServiciosEspecialesResponse GetServiciosEspeciales()
        {
            ServiciosEspecialesResponse response = new ServiciosEspecialesResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetServiciosEspeciales] method called.");

            try
            {
                List<ServicioEspecial> serviciosEspeciales = this.financiadoresService.GetServiciosEspeciales();
                response.ServicioEspecialList = EntityTranslator.GetServicioEspecialDtoArray(serviciosEspeciales);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetServiciosEspeciales] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        [WebMethod]
        public ServiciosEspecialesResponse GetServiciosEspecialesLocaciones(int financiadorId)
        {
            ServiciosEspecialesResponse response = new ServiciosEspecialesResponse();

            LoggerService.Log(LogLevel.Verbose, "[GetServiciosEspecialesLocaciones] method called.");

            try
            {
                List<ServicioEspecial> serviciosEspeciales = this.financiadoresService.GetServiciosEspecialesLocaciones(financiadorId);
                response.ServicioEspecialList = EntityTranslator.GetServicioEspecialDtoArray(serviciosEspeciales);
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred in [GetServiciosEspecialesLocaciones] method.");
                this.HandleException(ex, response);
            }

            return response;
        }

        private void HandleException(Exception ex, WebServiceBaseResponse response)
        {
            LoggerService.Log(LogLevel.Error, "Handled exception.", ex);

            if (ex is InsufficientPermissionsException)
            {
                response.ResponseCode = (byte)ResponseCode.InsufficientPermissions;
                response.Message = MobileHealthServiceResources.InsufficientPermissions;
                return;
            }

            if (ex is DataAccessException)
            {
                var dataAccessException = (DataAccessException)ex;

                switch (dataAccessException.DataBaseResultCode)
                {
                    case DataAccessErrorCode.UniqueKeyConstraintViolation:
                        response.ResponseCode = (byte)ResponseCode.EntityExists;
                        break;
                    case DataAccessErrorCode.ForeignKeyViolation:
                        response.ResponseCode = (byte)ResponseCode.EntityForeignKey;
                        break;
                    case DataAccessErrorCode.RecordNotFound:
                        response.ResponseCode = (byte)ResponseCode.EntityNotExists;
                        break;
                    default:
                        response.ResponseCode = (byte)ResponseCode.UnknownError;
                        break;
                }

                return;
            }

            response.ResponseCode = (byte)ResponseCode.UnknownError;
            response.Message = ex.Message;

            LoggerService.Log(LogLevel.Error, "Webservice response code is [{0}]. Message: {1}", response.ResponseCode, response.Message);
        }

        private void ValidateUser(string username, string password, int financiadorId)
        {
            if (!this.securityService.ValidateUserCredentials(username, password, financiadorId))
                throw new InsufficientPermissionsException();
        }
    }
}